import { Injectable } from '@nestjs/common';
import { DbService } from '../../db/db.service';
import { CacheService } from '@app/cache';
import CreateDto from './dto/create.dto';
import { DataSource, FindOptionsWhere, In, Like } from 'typeorm';
import * as dayjs from 'dayjs';
import { Merchant } from '@app/db/entity/Merchant';
import { Member } from '@app/db/common/entity/Member';
import { MemberService } from '../common/member/member.service';
import { ConfigureService } from '@app/configure';
import WorkException from '../../exception/work.exception';
import UpdateDto from './dto/update.dto';
import PageDto from './dto/page.dto';
import { MemberBelong } from '../../constant';

@Injectable()
export class MerchantService {
    constructor(
        private readonly db:DbService,
        private readonly cache:CacheService,
        protected readonly memberService:MemberService,
        private readonly config:ConfigureService,
        private readonly dataSource: DataSource,
    ){}

    async page(dto:PageDto, memberId:number) {
        let result:PaginationResult<Merchant & {admin? : Member}> = {
            total : 0,
            rows : []
        }

        let where:FindOptionsWhere<Merchant> = {

        }

        if(dto.name) {
            where.name = dto.name
        }


        result.total = await this.db.merchant.count(where)

        if(result.total > 0) {

            result.rows = await this.db.merchant.pagination(dto.pageNo, dto.pageSize, where, null, {id : 'DESC'})

            if(result.rows.length > 0) {
                const memberIds:number[] = result.rows.map(row => row.adminId)
                const members = await this.db.member.find({id : In(memberIds)}, {id : true, account : true, roles : true, name : true})
                result.rows.forEach(row => {
                    const member = members.find(v => v.id == row.adminId)
                    row.admin = member
                })
            }
        }

        return result
    }

    async add(dto:CreateDto, memberId:number, merchantId:number) {
        const profitRate = dto.profitRate;
        if(profitRate < 0 || profitRate > 1) return false

        const lockKey = 'c:m:' + dto.name
        const lock = await this.cache.set(lockKey, '1', 10, 'NX')
        if(!lock || (await this.db.merchant.isExistsByWhere({name : dto.name}))) throw new WorkException('已存在相同的名称')
        
        const time = dayjs().unix()
        
        const merchant:KeyToValueType<Merchant> = {
            profitRate : String(profitRate),
            name : dto.name,
            createTime : time,
            creatorId : memberId,
            parentId : merchantId,
        }

        if(dto.tel) {
            merchant.tel = dto.tel
        }

        if(dto.enable) {
            merchant.enable = dto.enable
        }

        if(dto.tel) {
            merchant.tel = dto.tel
        }

        // if(dto.remark) {
        //     merchant.remark = dto.remark
        // }

        const member:KeyToValueType<Member> = {
            account : dto.adminAccount,
            createTime : time,
            salt : this.memberService.generateSalt(),
            creatorId : memberId,
            belong : MemberBelong.MERCHANT
        }

        if(dto.enable) {
            member.enable = dto.enable
        }

        member.password = this.memberService.buildPassword(dto.adminPassword || this.config.get('memberDefaultPassword'), member.salt)

        if(dto.adminName) {
            member.name = dto.adminName
        }

        if(dto.roles) {
            member.roles = dto.roles
        }

        const queryRunner = this.dataSource.createQueryRunner();

        await queryRunner.connect();
        await queryRunner.startTransaction();
        try {
            const r1 = await queryRunner.manager.insert(Merchant, merchant)
            
            member.merchantId = r1.raw.insertId

            const r2 = await queryRunner.manager.insert(Member, member)

            await queryRunner.manager.update(Merchant, {id : member.merchantId}, {adminId : r2.raw.insertId})

            await queryRunner.commitTransaction();

            return true
        } catch (err) {
            await queryRunner.rollbackTransaction();
        } finally { 
            this.cache.del(lockKey)
            await queryRunner.release();
        }

        return false

    }

    async update(dto:UpdateDto) {
        const id = dto.id
        const row = await this.db.merchant.findOne({id : id}, {adminId : true, name : true, profitRate : true, tel : true, enable : true})
        if(!row) return false

        const updateData:KeyToValueType<Merchant> = {}

        for(let k in row) {
            if(k in dto) {
                if(dto[k] != row[k]) {
                    updateData[k] = dto[k]
                }
            }
        }

        if(updateData.name) {
            if((await this.db.merchant.isExistsByWhere({name : updateData.name}))) throw new WorkException('已存在相同的名称')
        }

        if(Object.keys(updateData).length > 0 || dto.roles != undefined) {

            const queryRunner = this.dataSource.createQueryRunner();

            await queryRunner.connect();
            await queryRunner.startTransaction();

            try {
                
                if(Object.keys(updateData).length > 0) {
                    await queryRunner.manager.update(Merchant, {id}, updateData)
                }


                if(dto.roles != undefined) {
                    await queryRunner.manager.update(Member, {id : row.adminId}, {roles : dto.roles})
                }

                await queryRunner.commitTransaction();
                return true

            } catch (err) {
                await queryRunner.rollbackTransaction();
            } finally { 
                await queryRunner.release();
            }

            return false
        }


        return true

    }

    async del(id:number) {
        let row = await this.db.merchant.findOne({ id }, ['id'])
        if(!row) return false
        return this.db.merchant.delete({id : id})
    }

    async resetPwd(id:number, password:string) {
        let row = await this.db.merchant.findOne({ id }, ['id', 'adminId'])
        if(!row) return false
        const member = await this.db.member.findOne({id : row.adminId}, {password : true, salt : true})
        if(this.memberService.buildPassword(password, member.salt) == member.password) throw new WorkException('密码不能和旧密码一样')
        member.salt = this.memberService.generateSalt()
        member.password = this.memberService.buildPassword(password, member.salt)
        
        return this.db.member.update({id : row.adminId}, member)
    }


    async queryMerchantByName(name:string, merchantId:number) {
        return this.db.merchant.find({parentId : merchantId, name : Like('%' + name + '%')})
    }
}
